/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb.giop;

/**
 * <p>Description: </p>
 * GIOP decode and CORBA exception implementation.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.huihoo.orbas.orb.cdr.CDRInputStream;
import org.huihoo.orbas.orb.cdr.CDROutputStream;
//import org.omg.GIOP.*;
//import org.omg.IOP.*;

public abstract class GiopWorker extends org.huihoo.orbas.orb.Worker {

	org.huihoo.orbas.orb.ORB orb;

	public CDRInputStream read() throws java.io.IOException,
			org.omg.CORBA.portable.ApplicationException,
			org.omg.CORBA.portable.RemarshalException {

		if (getTransport() == null) {
			// error
			System.out
					.println("Error:Worker's trasnport havent been initialized.");
			throw new java.io.IOException();
		}

		java.io.InputStream in = getTransport().getInputStream();

		int messageHeaderLength = 12;

		byte[] buffer = new byte[messageHeaderLength];
		int bytesRead = 0;

		try {
			if ((bytesRead = in.read(buffer, 0, messageHeaderLength)) == -1) {
				System.out.println("Connection closed by peer");

			}
		} catch (java.io.IOException e) {
			System.out.println("Communication Error:" + e.toString());
			e.printStackTrace();
			throw new java.io.IOException();
		}

		if (bytesRead != messageHeaderLength) {
			System.out.println("Protocol violation: Wrong GIOP Header Length");
			throw new java.io.IOException();
		}

		if (buffer[0] != 0x47 || buffer[1] != 0x49 || buffer[2] != 0x4f
				|| buffer[3] != 0x50) {
			System.out.println("Protocol violation: Malformed GIOP Header");
		}

		int messageSize;
		boolean isLittleEndian;

		CDRInputStream istream = new CDRInputStream(orb, buffer);

		// skip GIOP header magic number

		istream.read_long();
		org.omg.GIOP.Version version = org.omg.GIOP.VersionHelper.read(istream);

		// pre-declaration of local variavles
		byte reponseExpected;
		CDRInputStream headerInput;
		byte[] mesg;
		org.omg.GIOP.TargetAddress target;
		CDRInputStream msgIn = null;
		int type;

		switch (version.major) {
		case 1:
			switch (version.minor) {
			case 0:

				headerInput = new CDRInputStream(orb, buffer);

				headerInput.mark();
				org.omg.GIOP.MessageHeader_1_0 header0 = org.omg.GIOP.MessageHeader_1_0Helper
						.read(headerInput);

				isLittleEndian = header0.byte_order;

				headerInput.setLittleEndian(isLittleEndian);

				headerInput.reset();
				header0 = org.omg.GIOP.MessageHeader_1_0Helper
						.read(headerInput);
				// read good message size
				messageSize = header0.message_size;

				mesg = new byte[messageSize];

				try {
					if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
						System.out.println("Connection closed by peer");

					}
				} catch (java.io.IOException e) {
					System.out.println("Communication Error:" + e.toString());
					e.printStackTrace();
					throw new java.io.IOException();
				}

				if (bytesRead != messageSize) {
					System.out.println("GIOP Message not of Correct Length");
					throw new java.io.IOException();
				}

				msgIn = new CDRInputStream(orb, mesg);
				msgIn.setLittleEndian(isLittleEndian);

				type = (int) header0.message_type;
				switch (type) {
				case org.omg.GIOP.MsgType_1_0._Request:
					org.omg.GIOP.RequestHeader_1_0 request = org.omg.GIOP.RequestHeader_1_0Helper
							.read(msgIn);

					target = new org.omg.GIOP.TargetAddress();
					target.object_key(request.object_key);

					if (request.response_expected == true) {
						reponseExpected = 1;
					} else {
						reponseExpected = 0;
					}

					handleRequest(request.request_id, request.operation,
							request.service_context, target, reponseExpected,
							msgIn);
					break;

				case org.omg.GIOP.MsgType_1_0._Reply:
					org.omg.GIOP.ReplyHeader_1_0 reply = org.omg.GIOP.ReplyHeader_1_0Helper
							.read(msgIn);

					handleReply(reply.request_id, reply.reply_status.value(),
							reply.service_context, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_0._LocateRequest:
					org.omg.GIOP.LocateRequestHeader_1_0 locateRequest = org.omg.GIOP.LocateRequestHeader_1_0Helper
							.read(msgIn);

					target = new org.omg.GIOP.TargetAddress();
					target.object_key(locateRequest.object_key);
					handleLocateRequest(locateRequest.request_id, target, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_0._LocateReply:
					org.omg.GIOP.LocateReplyHeader_1_0 locateReply = org.omg.GIOP.LocateReplyHeader_1_0Helper
							.read(msgIn);

					handleLocateReply(locateReply.request_id,
							locateReply.locate_status.value(), msgIn);
					break;

				case org.omg.GIOP.MsgType_1_0._CancelRequest:
					org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
							.read(msgIn);

					handleCancelRequest(cancelRequest.request_id);

				case org.omg.GIOP.MsgType_1_0._CloseConnection:

					handleCloseConnection();
					break;

				case org.omg.GIOP.MsgType_1_0._MessageError:
					// Error
					handleMessageError();
					throw new java.io.IOException("GIOP Message Error");
					// break;

				default:
					// error
				}

				break; // minor version is 0

			case 1:

				headerInput = new CDRInputStream(orb, buffer);

				headerInput.mark();
				org.omg.GIOP.MessageHeader_1_1 header1 = org.omg.GIOP.MessageHeader_1_1Helper
						.read(headerInput);

				if ((header1.flags & 0x01) == 0x1) {
					isLittleEndian = true;
				} else {
					isLittleEndian = false;
				}

				headerInput.setLittleEndian(isLittleEndian);

				headerInput.reset();
				header1 = org.omg.GIOP.MessageHeader_1_1Helper
						.read(headerInput);
				messageSize = header1.message_size;

				mesg = new byte[messageSize];

				try {
					if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
						System.out.println("Connection closed by peer");
					}
				} catch (java.io.IOException e) {
					System.out.println("Communication Error:" + e.toString());
					e.printStackTrace();
					throw new java.io.IOException();
				}

				if (bytesRead != messageSize) {
					System.out.println("GIOP msg not of correct length");
					throw new java.io.IOException();
				}

				msgIn = new CDRInputStream(orb, mesg);
				msgIn.setLittleEndian(isLittleEndian);

				type = (int) header1.message_type;
				switch (type) {
				case org.omg.GIOP.MsgType_1_1._Request:
					org.omg.GIOP.RequestHeader_1_1 request = org.omg.GIOP.RequestHeader_1_1Helper
							.read(msgIn);

					target = new org.omg.GIOP.TargetAddress();
					target.object_key(request.object_key);

					if (request.response_expected == true) {
						reponseExpected = 1;
					} else {
						reponseExpected = 0;
					}

					handleRequest(request.request_id, request.operation,
							request.service_context, target, reponseExpected,
							msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._Reply:
					org.omg.GIOP.ReplyHeader_1_0 reply = org.omg.GIOP.ReplyHeader_1_1Helper
							.read(msgIn);

					handleReply(reply.request_id, reply.reply_status.value(),
							reply.service_context, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._LocateRequest:
					org.omg.GIOP.LocateRequestHeader_1_0 locateRequest = org.omg.GIOP.LocateRequestHeader_1_1Helper
							.read(msgIn);

					target = new org.omg.GIOP.TargetAddress();
					target.object_key(locateRequest.object_key);
					handleLocateRequest(locateRequest.request_id, target, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._LocateReply:
					org.omg.GIOP.LocateReplyHeader_1_0 locateReply = org.omg.GIOP.LocateReplyHeader_1_1Helper
							.read(msgIn);

					handleLocateReply(locateReply.request_id,
							locateReply.locate_status.value(), msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._CancelRequest:
					org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
							.read(msgIn);

					handleCancelRequest(cancelRequest.request_id);

				case org.omg.GIOP.MsgType_1_1._CloseConnection:
					handleCloseConnection();
					break;

				case org.omg.GIOP.MsgType_1_1._MessageError:
					handleMessageError();
					break;

				case org.omg.GIOP.MsgType_1_1._Fragment:
					handleFragment();
					break;

				default:
					// error
				}

				break; // minor version is 1

			case 2:

				headerInput = new CDRInputStream(orb, buffer);
				headerInput.mark();
				org.omg.GIOP.MessageHeader_1_1 header2 = org.omg.GIOP.MessageHeader_1_2Helper
						.read(headerInput);

				if ((header2.flags & 0x01) == 0x1) {
					isLittleEndian = true;
				} else {
					isLittleEndian = false;
				}

				headerInput.setLittleEndian(isLittleEndian);

				headerInput.reset();
				header2 = org.omg.GIOP.MessageHeader_1_2Helper
						.read(headerInput);
				messageSize = header2.message_size;

				mesg = new byte[messageSize];

				try {
					if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
						System.out.println("Connection closed by peer");
					}
				} catch (java.io.IOException e) {
					System.out.println("Communication Error:" + e.toString());
					e.printStackTrace();
					throw new java.io.IOException();
				}

				if (bytesRead != messageSize) {
					System.out.println("GIOP msg not of correct length");
					throw new java.io.IOException();
				}

				msgIn = new CDRInputStream(orb, mesg);
				msgIn.setLittleEndian(isLittleEndian);

				type = (int) header2.message_type;
				switch (type) {
				case org.omg.GIOP.MsgType_1_1._Request:
					org.omg.GIOP.RequestHeader_1_2 request = org.omg.GIOP.RequestHeader_1_2Helper
							.read(msgIn);

					handleRequest(request.request_id, request.operation,
							request.service_context, request.target,
							request.response_flags, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._Reply:
					org.omg.GIOP.ReplyHeader_1_2 reply = org.omg.GIOP.ReplyHeader_1_2Helper
							.read(msgIn);

					handleReply(reply.request_id, reply.reply_status.value(),
							reply.service_context, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._LocateRequest:
					org.omg.GIOP.LocateRequestHeader_1_2 locateRequest = org.omg.GIOP.LocateRequestHeader_1_2Helper
							.read(msgIn);

					handleLocateRequest(locateRequest.request_id,
							locateRequest.target, msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._LocateReply:
					org.omg.GIOP.LocateReplyHeader_1_2 locateReply = org.omg.GIOP.LocateReplyHeader_1_2Helper
							.read(msgIn);

					handleLocateReply(locateReply.request_id,
							locateReply.locate_status.value(), msgIn);
					break;

				case org.omg.GIOP.MsgType_1_1._CancelRequest:
					org.omg.GIOP.CancelRequestHeader cancelRequest = org.omg.GIOP.CancelRequestHeaderHelper
							.read(msgIn);

					handleCancelRequest(cancelRequest.request_id);

				case org.omg.GIOP.MsgType_1_1._CloseConnection:
					handleCloseConnection();
					break;

				case org.omg.GIOP.MsgType_1_1._MessageError:
					handleMessageError();
					break;

				case org.omg.GIOP.MsgType_1_1._Fragment:
					handleFragment();
					break;

				default:
					// error
				}

				break; // minor version is 2

			default:
			}

			break; // major version is 1

		default:
			// error
		}

		return msgIn;

	}

	public void write(org.omg.CORBA.portable.OutputStream ostream)
			throws java.io.IOException {
		if (getTransport() == null) {
			// error
			System.out.println("Worker's trasnport havent been initialized.");
		}

		try {
			java.io.OutputStream out = getTransport().getOutputStream();

			out.write(((CDROutputStream) ostream).getBuffer(), 0,
					((CDROutputStream) ostream).getLength());
			out.flush();
		} catch (java.io.IOException ex) {
			System.out.println("Communication Error: " + ex.toString());
		}
	}

	public void handleRequest(int request_id, String Operation,
			org.omg.IOP.ServiceContext[] context,
			org.omg.GIOP.TargetAddress target, byte response_flags,
			CDRInputStream in) {

	}

	public void handleReply(int request_id, int reply_status,
			org.omg.IOP.ServiceContext[] context, CDRInputStream in)
			throws org.omg.CORBA.portable.ApplicationException,
			org.omg.CORBA.portable.RemarshalException {

		switch (reply_status) {
		case org.omg.GIOP.ReplyStatusType_1_2._NO_EXCEPTION:
			return;

		case org.omg.GIOP.ReplyStatusType_1_2._USER_EXCEPTION:

			// how to abtain Exception id here?
			// we can clone the inputstream and
			// read id from inputstream then pass
			// the old stream.Use mark() for better
			// performance.

			in.mark();

			String id = in.read_string();

			in.reset();

			throw new org.omg.CORBA.portable.ApplicationException(id, in);

		case org.omg.GIOP.ReplyStatusType_1_2._SYSTEM_EXCEPTION:
			// Do NOT propagate this exception to
			// client stubs, this exception should
			// be dealed by ORB.

			String systemExId = in.read_string();
			int minor = in.read_long();
			int status = in.read_long();

			// deal with minor code

			int minorCode = minor & 0x0FFF;

			System.out.println("SystemException received from remote : "
					+ systemExId);
			System.out.println("Minor code: " + minorCode);

			switch (status) {
			case 0:
				System.out.println("Completion Status : Yes");
				break;
			case 1:
				System.out.println("Completion Status : No");
				break;
			case 2:
				System.out.println("Completion Status : Maybe");
				break;
			default:
				// invalid status
				System.out.println("Invalid Completion Status");
			}

			// throw exception definitely
			handleException(systemExId, minorCode, status);

			break;

		case org.omg.GIOP.ReplyStatusType_1_2._LOCATION_FORWARD:
			// what to do?throw a location forward exception?
			break;

		case org.omg.GIOP.ReplyStatusType_1_2._LOCATION_FORWARD_PERM:
			// not support now
			break;

		case org.omg.GIOP.ReplyStatusType_1_2._NEEDS_ADDRESSING_MODE:
			// not support now
			break;

		default:
			// error!
		}
	}

	public void handleLocateRequest(int request_id,
			org.omg.GIOP.TargetAddress target, CDRInputStream in) {

	}

	public void handleLocateReply(int request_id, int locate_status,
			CDRInputStream in) {

	}

	public void handleCancelRequest(int request_id) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void handleCloseConnection() {
		throw new org.omg.CORBA.NO_IMPLEMENT();

	}

	public void handleMessageError() {
		System.out.println("GIOP Error Message received");

	}

	public void handleFragment() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void handleException(String id, int minorCode, int statusValue) {

		// now we support the following standard exception:
		// UNKNOWN, BAD_PARAM, NO_MEMORY, IMP_LIMIT, COMM_FAILURE,
		// INV_OBJREF, NO_PERMISSION, INTERNAL, MARSHAL, INITIALIZE,
		// BAD_TYPECODE, BAD_OPERATION, NO_RESOURCES, NO_RESPONSE,
		// PERSIST_STORE, BAD_INV_ORDER, TRANSIENT, FREE_MEM,
		// INV_IDENT, INV_FLAG, INTF_REPOS, BAD_CONTEXT, OBJ_ADAPTER,
		// DATA_CONVERSION, OBJECT_NOT_EXIST, TRANSACTION_REQUIRED,
		// TRANSACTION_ROLLEDBACK, INVALID_TRANSACTION, INV_POLICY,
		// CODESET_INCOMPATIBLE, NO_IMPLEMENT, REBIND, TIMEOUT,
		// TRANSACTION_UNAVAILABLE, TRANSACTION_MODE, BAD_QOS

		org.omg.CORBA.CompletionStatus status = org.omg.CORBA.CompletionStatus
				.from_int(statusValue);

		if (id.equals("IDL:org/omg/CORBA/BAD_PARAM:1.0")) {
			throw new org.omg.CORBA.BAD_PARAM(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/NO_MEMORY:1.0")) {
			throw new org.omg.CORBA.NO_MEMORY(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/IMP_LIMIT:1.0")) {
			throw new org.omg.CORBA.IMP_LIMIT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/COMM_FAILURE:1.0")) {
			throw new org.omg.CORBA.COMM_FAILURE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INV_OBJREF:1.0")) {
			throw new org.omg.CORBA.INV_OBJREF(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/NO_PERMISSION:1.0")) {
			throw new org.omg.CORBA.NO_PERMISSION(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INTERNAL:1.0")) {
			throw new org.omg.CORBA.INTERNAL(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/MARSHAL:1.0")) {
			throw new org.omg.CORBA.MARSHAL(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INITIALIZE:1.0")) {
			throw new org.omg.CORBA.INITIALIZE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/BAD_TYPECODE:1.0")) {
			throw new org.omg.CORBA.BAD_TYPECODE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/BAD_OPERATION:1.0")) {
			throw new org.omg.CORBA.BAD_OPERATION(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/NO_RESOURCES:1.0")) {
			throw new org.omg.CORBA.NO_RESOURCES(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/NO_RESPONSE:1.0")) {
			throw new org.omg.CORBA.NO_RESPONSE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/PERSIST_STORE:1.0")) {
			throw new org.omg.CORBA.PERSIST_STORE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/BAD_INV_ORDER:1.0")) {
			throw new org.omg.CORBA.BAD_INV_ORDER(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TRANSIENT:1.0")) {
			throw new org.omg.CORBA.TRANSIENT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/FREE_MEM:1.0")) {
			throw new org.omg.CORBA.FREE_MEM(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INV_IDENT:1.0")) {
			throw new org.omg.CORBA.INV_IDENT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INV_FLAG:1.0")) {
			throw new org.omg.CORBA.INV_FLAG(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INTF_REPOS:1.0")) {
			throw new org.omg.CORBA.INTF_REPOS(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/BAD_CONTEXT:1.0")) {
			throw new org.omg.CORBA.BAD_CONTEXT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/OBJ_ADAPTER:1.0")) {
			throw new org.omg.CORBA.OBJ_ADAPTER(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/DATA_CONVERSION:1.0")) {
			throw new org.omg.CORBA.DATA_CONVERSION(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/OBJECT_NOT_EXIST:1.0")) {
			throw new org.omg.CORBA.OBJECT_NOT_EXIST(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TRANSACTION_REQUIRED:1.0")) {
			throw new org.omg.CORBA.TRANSACTION_REQUIRED(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TRANSACTION_ROLLEDBACK:1.0")) {
			throw new org.omg.CORBA.TRANSACTION_ROLLEDBACK(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INVALID_TRANSACTION:1.0")) {
			throw new org.omg.CORBA.INVALID_TRANSACTION(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/INV_POLICY:1.0")) {
			throw new org.omg.CORBA.INV_POLICY(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/CODESET_INCOMPATIBLE:1.0")) {
			throw new org.omg.CORBA.CODESET_INCOMPATIBLE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/NO_IMPLEMENT:1.0")) {
			throw new org.omg.CORBA.NO_IMPLEMENT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/REBIND:1.0")) {
			throw new org.omg.CORBA.REBIND(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TIMEOUT:1.0")) {
			throw new org.omg.CORBA.TIMEOUT(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TRANSACTION_UNAVAILABLE:1.0")) {
			throw new org.omg.CORBA.TRANSACTION_UNAVAILABLE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/TRANSACTION_MODE:1.0")) {
			throw new org.omg.CORBA.TRANSACTION_MODE(minorCode, status);
		} else if (id.equals("IDL:org/omg/CORBA/BAD_QOS:1.0")) {
			throw new org.omg.CORBA.BAD_QOS(minorCode, status);
		} else {
			throw new org.omg.CORBA.UNKNOWN(minorCode, status);
		}
	}

}